package com.example.java2024_5_1searcher.searcher;



import org.ansj.domain.Term;
import org.ansj.splitWord.analysis.ToAnalysis;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

//操作查询操作的代码

public class DoSearcher {
    Index index=new Index();
    static String STOP_WORD_PATH =null;
    static {
        if(Flag.flag){
          STOP_WORD_PATH = "E:\\java_jdk\\jdk-8u411-docs-all\\docs\\api\\stop_word.txt";
        }else {
            STOP_WORD_PATH ="/root/do_searcher/stop_word.txt";
        }

    }

    private HashSet<String> stopWords = new HashSet<>();
    public DoSearcher() {
        index.load();
      loadStopWords();
    }

    //searcher
    public ArrayList<Result> Search(String content){
        //先对查询内容分词，得到内容相关词后
        List<Term> oldterms = ToAnalysis.parse(content).getTerms();
        for(Term term:oldterms){
            System.out.println(term.getName());
        }
        List<Term> terms=new ArrayList<>();
        for(Term term:oldterms){
            if(stopWords.contains(term.getName())){
                System.out.println(term.getName()+"1");
                continue;
            }
            terms.add(term);
        }
        //遍历,在倒排索引中寻找
        ArrayList<ArrayList<Weight>> ans1=new ArrayList<>();
        for(Term term:terms){
            //查询结果就是与查询内容相关性的文档
            ArrayList<Weight> arrayList=index.invertedIndex(term.getName());
            ans1.add(arrayList);
        }
        //权重合并，因为有两个词出现在同一个文档中的可能性
        ArrayList<Weight> ans=mergeResult(ans1);
        //现在得到的是与查询词相关的所有文档，进行排序
       ans.sort(new Comparator<Weight>() {
           @Override
           public int compare(Weight o1, Weight o2) {
               return o2.getWeight() - o1.getWeight();
           }
       });
        ArrayList<Result> results=new ArrayList<>();
        //现在得到的是相关性从大到小排序的文档再根据正排索引查找结果并包装
        for(Weight weight:ans){
            DocInfo docInfo=index.forwardIndex(weight.getId());
          Result result=pack(docInfo,docInfo.text,terms);
          results.add(result);
        }
        return results;
    }
    private ArrayList<Weight> mergeResult(ArrayList<ArrayList<Weight>> ans1) {
        //多路合并思路，构造一个优先级队列（排序规则是按照元素的id大小），将ans1进行排序按照ans1的每个元素id，
        // 将ans1的每一行的首元素都加进去，按照然后将他们一个个取出来然后取出来的那一行的光标后移一位
        for(ArrayList<Weight> weightArrayList:ans1){
            Collections.sort(weightArrayList, new Comparator<Weight>() {
                @Override
                public int compare(Weight o1, Weight o2) {
                    return o1.getId()-o2.getId();
                }
            });
        }
        //    target 表示合并的结果
        ArrayList<Weight> target = new ArrayList<>();
        PriorityQueue<Pos> priorityQueue= new PriorityQueue<>(new Comparator<Pos>() {
            @Override
            public int compare(Pos o1, Pos o2) {
                // 先根据 pos 值找到对应的 Weight 对象, 再根据 Weight 的 docId 来排序
                Weight w1 = ans1.get(o1.row).get(o1.col);
                Weight w2 = ans1.get(o2.row).get(o2.col);
                return w1.getId() - w2.getId();
            }
        });
        //初始化优先级队列,将每一行首位加进去
        for(int i=0;i<ans1.size();i++){
            priorityQueue.add(new Pos(i,0));
        }
        while (!priorityQueue.isEmpty()){
            Pos pos=priorityQueue.poll();
            Weight weight=ans1.get(pos.row).get(pos.col);
            if(target.size() > 0){
                //如果长度大于零就取出上一次的元素，和这次添加的对比
                Weight weight1=target.get(target.size()-1);
                if(weight1.getId()==weight.getId()){
                    weight1.setWeight(weight.getWeight()+weight1.getWeight());
                }else {
                    target.add(weight);
                }
            }else {
                target.add(weight);
            }
            Pos newPos = new Pos(pos.row, pos.col + 1);
            if (newPos.col >= ans1.get(newPos.row).size()) {
                // 如果移动光标之后, 超出了这一行的列数, 就说明到达末尾了.
                // 到达末尾之后说明这一行就处理完毕了~~
                continue;
            }
            priorityQueue.offer(newPos);
        }

        return  target;
    }

    private String GenDesc(String content, List<Term> terms) {
        //逻辑是什么，首先遍历分词结果（查询词，看哪个词在里面存在）
        int flag=-1;
        //表示词是否在正文出现
        for(Term term:terms){
            String word=term.getName();
            // 此处需要的是 "全字匹配", 让 word 能够独立成词, 才要查找出来, 而不是只作为词的一部分.
            // 此处的全字匹配的实现并不算特别严谨. 更严谨的做法, 可以使用正则表达式.
            content = content.toLowerCase().replaceAll("\\b" + word + "\\b", " " + word + " ");
            flag = content.indexOf(" " + word + " ");
            if (flag >= 0) {
                // 找到了位置
                break;
            }
        }
        if (flag == -1) {
            // 所有的分词结果都不在正文中存在.
            // 因此这是属于比较极端的情况~
            // 此时就直接返回一个空的描述了.
            // 或者也可以直接取正文的前 160 个字符作为描述.
            if (content.length() > 160) {
                return content.substring(0, 160) + "...";
            }
            return content;
        }
            String desc = "";
            int descBeg = flag < 60 ? 0 : flag - 60;
            if (descBeg + 160 > content.length()) {
                desc = content.substring(descBeg);
            } else {
                desc = content.substring(descBeg, descBeg + 160) + "...";
            }

            // 在此处加上一个替换操作. 把描述中的和分词结果相同的部分, 给加上一层 <i> 标签. 就可以通过 replace 的方式来实现.
            for (Term term : terms) {
                String word = term.getName();
                // 注意, 此处要进行全字匹配. 也就是当查询词为 List 的时候 不能把 ArrayList 中的 List 给单独标红
                desc = desc.replaceAll("(?i) " + word + " ", "<i> " + word + " </i>");
            }
            return desc;


    }
    public void loadStopWords() {
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(STOP_WORD_PATH))) {
            while (true) {
                String line = bufferedReader.readLine();
                if (line == null) {
                    // 读取文件完毕!
                    break;
                }
                stopWords.add(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public Result pack(DocInfo docInfo,String content, List<Term> terms){
        Result result=new Result(docInfo.getTitle(),GenDesc(content,terms),docInfo.getUrl());
        return result;
    }
}
